package org.common.myhttpclient;

import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;

import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.utils.HttpClientUtils;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * http or https 请求工具类
 */
public class HttpClientUtil{
	
	private static Logger log= LoggerFactory.getLogger(HttpClientUtil.class);
	
	/**
	 * POST JSON 请求
	 * @param url
	 * @param jsonData 参数为JSON字符串
	 * @return
	 */
	public static String doJsonPost(String url,String jsonData){
		if(StringUtils.isBlank(url)){
			throw new NullPointerException("doJsonPost request url not allow null or ''! ");
		}
		long beginTime=System.currentTimeMillis();
		HttpPost httpPost = new HttpPost(url);
		try {
			httpPost.addHeader("Content-type","application/json; charset=utf-8"); 
			httpPost.setEntity(new StringEntity(jsonData,StandardCharsets.UTF_8));
			String response=InnerHttpClient.sendRequest(httpPost);
			long endTime=System.currentTimeMillis();
			log.info("doJsonPost request url:{} successed!reqParams is :{},response content is :{},time-consuming is:{}ms!",url,jsonData,response,endTime-beginTime);
			return response;
		} catch (Exception e) {
			log.error("doJsonPost request url:{} failed!reqParams is :{},exception content is :",url,jsonData,e);
			return null;
		}
	}
	
	/**
	 * POST 请求,可附带参数
	 * @param url
	 * @param reqParams
	 * @return
	 */
	public static String doPost(String url, Map<String, String> reqParams){
		if(StringUtils.isBlank(url)){
			throw new NullPointerException("doPost request url not allow null or ''! ");
		}
		long beginTime=System.currentTimeMillis();
		List <NameValuePair> nvps = new ArrayList <NameValuePair>();
		if(reqParams!=null&&!reqParams.isEmpty()){
			for(String k:reqParams.keySet()){
				nvps.add(new BasicNameValuePair(k,reqParams.get(k)));
			}
		}
		HttpPost httpPost = new HttpPost(url);
		try {
			httpPost.setEntity(new UrlEncodedFormEntity(nvps,StandardCharsets.UTF_8));
			String response=InnerHttpClient.sendRequest(httpPost);
			long endTime=System.currentTimeMillis();
			log.info("doPost request url:{} successed!reqParams is :{},response content is :{},time-consuming is:{}ms!",url,reqParams,response,endTime-beginTime);
			return response;
		} catch (Exception e) {
			log.error("doPost request url:{} failed!reqParams is :{},exception content is :",url,reqParams,e);
			return null;
		}
	}
	
	/**
	 * GET 请求
	 * @param url
	 * @return
	 */
	public static String doGet(String url){
		return doGet(url, null);
	}
	
	/**
	 * GET 请求，附带有提供的参数
	 * @param url
	 * @param reqParams 请求参数
	 * @return
	 */
	public static String doGet(String url, Map<String, String> reqParams){
		if(StringUtils.isBlank(url)){
			throw new NullPointerException("doGet request url not allow null or ''! ");
		}
		long beginTime=System.currentTimeMillis();
		StringBuilder builder=new StringBuilder();
		if(reqParams!=null&&!reqParams.isEmpty()){
			for(String k:reqParams.keySet()){
				if(builder.length()>0){
					builder.append("&");
				}
				builder.append(k).append("=");
				if(StringUtils.isNotBlank(reqParams.get(k))){
					builder.append(reqParams.get(k));
				}
			}
		}
		String params=builder.toString();
		if(params.length()>0){
			url+=url.indexOf("?")!=-1?"&"+params:"?"+params;
		}
		try {
			URL u=new URL(url);
			HttpGet httpGet=new HttpGet(new URI(u.getProtocol(),u.getUserInfo(),u.getHost(),u.getPort(),u.getPath(), u.getQuery(),null));
			String response=InnerHttpClient.sendRequest(httpGet);
			long endTime=System.currentTimeMillis();
			log.info("doGet request url:{} successed!response content is :{},time-consuming is:{}ms!",url,response,endTime-beginTime);
			return response;
		} catch (Exception e) {
			log.error("doGet request url:{} failed!exception content is :",url,e);
			return null;
		}
	}
	
	/**
	 * 请求客户端实现类
	 */
	static class InnerHttpClient{
		//http clilent中从connetcion pool中获得一个connection的超时时间
		final static int REQUEST_TIME_OUT=5000;
		//链接建立的超时时间
		final static int CONNECT_TIME_OUT=10000;
		//响应超时时间，超过此时间不再读取响应
		final static int SOCKET_TIME_OUT=60000;
		/**
		 * 请求配置
		 * @return
		 */
		static RequestConfig config(){
			return RequestConfig.custom()
					.setConnectionRequestTimeout(0)
					.setConnectTimeout(0)
					.setSocketTimeout(0)
					.build();
		}
		/**
		 * 设置请求协议，针对HTTPS协议信任所有证书，同时忽略对HOSTNAME的校验
		 * @return
		 * @throws KeyManagementException
		 * @throws NoSuchAlgorithmException
		 * @throws KeyStoreException
		 */
		static Registry<ConnectionSocketFactory> registry() throws Exception{
			SSLContext sslcontext = new SSLContextBuilder().loadTrustMaterial(new TrustStrategy() {  
	            public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {  
	                return true;  
	            }  
	        }).build();
			// Create a registry of custom connection socket factories for supported
	        // protocol schemes.
			return RegistryBuilder.<ConnectionSocketFactory>create()
	            .register("http", PlainConnectionSocketFactory.INSTANCE)
	            .register("https", new SSLConnectionSocketFactory(sslcontext,new HostnameVerifier() {
					
					@Override
					public boolean verify(String hostname, SSLSession session) {
						return true;
					}
					
				})).build();
		}
		/**
		 * connect链接池管理
		 * @return
		 * @throws KeyManagementException
		 * @throws NoSuchAlgorithmException
		 * @throws KeyStoreException
		 */
		static PoolingHttpClientConnectionManager connectManager() throws Exception{
			return new PoolingHttpClientConnectionManager(registry());
		}
		/**
		 * 创建请求客户端
		 * @return
		 * @throws Exception 
		 */
		static HttpClient create() throws Exception{
			return HttpClients.custom()
					.setDefaultRequestConfig(config())
					.setConnectionManager(connectManager()).build();
		}
		
		/**
		 * 发送请求模板
		 * @param requestDeal
		 * @return
		 * @throws Exception 
		 */
		static String sendRequest(HttpUriRequest uriRequest) throws Exception{
			HttpClient httpClient=create();
			try {
				// Create a custom response handler
	            ResponseHandler<String> responseHandler = new ResponseHandler<String>() {

	                @Override
	                public String handleResponse(final HttpResponse response) throws ClientProtocolException, IOException {
	                    int status = response.getStatusLine().getStatusCode();
	                    if (status >= HttpStatus.SC_OK && status < HttpStatus.SC_MULTIPLE_CHOICES) {
	                        HttpEntity entity = response.getEntity();
	                        return entity != null ? EntityUtils.toString(entity,StandardCharsets.UTF_8) : null;
	                    } else {
	                        throw new ClientProtocolException("Unexpected response status: " + status);
	                    }
	                }
	                
	            };
	           return httpClient.execute(uriRequest, responseHandler);
			} catch (Exception e) {
				throw e;
			}finally{
				HttpClientUtils.closeQuietly(httpClient);
			}
		}
	}
}
